//! 上链文本/上链数据消息
//!

use crate::{
    bean::message::{base_message::BaseMessage, fee, message_type::MessageType},
    core::{constant, event_body::BigInt},
    error::{error_msg, Error},
    mnemonic::Mnemonic,
    utils::sign,
};
use base64::Engine;
use crypto::{
    digest::Digest,
    sha3::{Sha3, Sha3Mode},
};
use hdwallet::secp256k1::Secp256k1;
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};

#[derive(Debug, Clone, Default)]
pub struct TextMessage {
    base_message: BaseMessage,
    // 整个消息的哈希值（type,fromAddress,toAddress,fee,timestamp,context）
    hash: String,
    // 接受地址（可选字段）
    to_address: String,
    // 手续费(可选字段，根据场景和业务方需求设置。)
    fee: BigInt,
    // NRG 价格
    nrg_price: BigInt,
    // 上链文本/上链数据
    context: Vec<u8>,
}

impl Deref for TextMessage {
    type Target = BaseMessage;

    fn deref(&self) -> &Self::Target {
        &self.base_message
    }
}

impl DerefMut for TextMessage {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.base_message
    }
}

impl TextMessage {
    pub fn new() -> Self {
        Self::default()
    }

    /// words: 助记词
    /// passphrase: 生成助记词的密码串
    /// from_address: 发送地址
    /// context: 文本数据、上链数据内容
    /// fee: 数据上链手续费
    /// to_address: 接受地址(可选)
    /// nrg_price: 手续费NRG价格
    pub fn new_(
        words: &str,
        passphrase: &str,
        from_address: &str,
        context: &[u8],
        fee: &BigInt,
        to_address: &str,
        nrg_price: &BigInt,
    ) -> Result<Self, Error> {
        if words.is_empty() || words.split(" ").count() != constant::MNEMONIC_WORD_SIZE {
            return Err(error_msg("words is illegal"));
        }

        if from_address.is_empty() {
            return Err(error_msg("from_address is illegal"));
        }

        if context.is_empty() {
            return Err(error_msg("context is illegal"));
        }
        let mut new_one = Self::default();
        new_one.set_msg_type(MessageType::Text);
        new_one.set_from_address(from_address);
        new_one.set_context(context);
        new_one.set_to_address(to_address);
        if constant::NEED_FEE {
            let calc_fee = fee::calculate_fee_by_len(context.len() as i64);
            let tmp_fee = if fee.deref() > &calc_fee {
                fee.clone()
            } else {
                BigInt::from(calc_fee)
            };
            new_one.set_fee(&tmp_fee);
            new_one.set_nrg_price(nrg_price);
        }
        new_one.set_timestamp(chrono::Local::now().timestamp());
        new_one.sign_message(words, passphrase)?;
        Ok(new_one)
    }

    fn sign_message(&mut self, words: &str, passphrase: &str) -> Result<(), Error> {
        let mnemonic = Mnemonic::new(words, passphrase)?;
        let keys = mnemonic.keypair(44, 0, 0, 0, 0.into())?;
        let private_key = keys.secret_key();
        let public_key = private_key.public_key(&Secp256k1::new());
        let public_key_str =
            base64::engine::general_purpose::STANDARD.encode(public_key.serialize());
        self.set_pub_key(&public_key_str);
        let msg = self.get_message()?;
        self.set_signature(&sign::sign(&msg, &private_key)?);
        let mut sha = Sha3::new(Sha3Mode::Sha3_256);
        let mut hash_value = [0u8; 32];
        sha.input(&base64::engine::general_purpose::STANDARD.decode(&self.signature()[4..])?);
        sha.result(&mut hash_value);
        self.hash = base64::engine::general_purpose::STANDARD.encode(hash_value);
        Ok(())
    }

    pub fn get_message(&self) -> Result<String, Error> {
        let mut inner = Inner::default();
        if self.from_address().is_empty() {
            return Err(error_msg("from_address is illegal"));
        }
        inner.from_address = self.from_address().to_string();

        if self.context().is_empty() {
            return Err(error_msg("context is illegal"));
        }
        inner.context = base64::engine::general_purpose::STANDARD.encode(self.context());

        if chrono::Local::now().timestamp() - self.timestamp()
            > constant::MESSAGE_PRESEND_TIME_OUT as i64
            || self.timestamp() > chrono::Local::now().timestamp()
        {
            return Err(error_msg("timestamp is illegal"));
        }
        inner.timestamp = self.timestamp();
        if self.pub_key().is_empty() {
            return Err(error_msg("pubkey is illegal"));
        }
        inner.pubkey = self.pub_key().to_string();

        if self.msg_type() != MessageType::Text {
            return Err(error_msg("type is illegal"));
        }
        inner.r#type = self.msg_type();
        if !self.to_address().is_empty() {
            inner.to_address = self.to_address().to_string();
        }

        if self.fee().deref() > &num_bigint::BigInt::from(0) {
            inner.fee = self.fee().clone();
        }
        if self.signature().is_empty() {
            inner.signature = None;
            Ok(serde_json::to_string(&inner)?)
        } else {
            inner.signature = Some(self.signature().to_string());
            let msg = MessageJson { message: inner };
            Ok(serde_json::to_string(&msg)?)
        }
    }

    pub fn hash(&self) -> &str {
        &self.hash
    }

    pub fn to_address(&self) -> &str {
        &self.to_address
    }

    pub fn fee(&self) -> &BigInt {
        &self.fee
    }

    pub fn nrg_price(&self) -> &BigInt {
        &self.nrg_price
    }

    pub fn context(&self) -> &[u8] {
        &self.context
    }

    pub fn set_hash(&mut self, hash: &str) {
        self.hash = hash.to_string();
    }

    pub fn set_to_address(&mut self, to_address: &str) {
        self.to_address = to_address.to_string();
    }

    pub fn set_fee(&mut self, fee: &BigInt) {
        self.fee = fee.clone();
    }

    pub fn set_nrg_price(&mut self, nrg_price: &BigInt) {
        self.nrg_price = nrg_price.clone();
    }

    pub fn set_context(&mut self, context: &[u8]) {
        self.context = context.to_vec();
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MessageJson {
    message: Inner,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Inner {
    from_address: String,
    context: String,
    timestamp: i64,
    pubkey: String,
    r#type: MessageType,
    to_address: String,
    fee: BigInt,
    #[serde(skip_serializing_if = "Option::is_none")]
    signature: Option<String>,
}

#[cfg(test)]
mod tests {
    use crate::{bean::message::text_message::TextMessage, mnemonic::Mnemonic};

    #[test]
    fn test_text_message() {
        let mnemonic = Mnemonic::new("", "wawa").unwrap();
        let tm = TextMessage::new_(
            mnemonic.mnemonic(),
            "wawa",
            "192.168.0.1",
            &[1, 2, 3, 4, 5, 6, 8],
            &num_bigint::BigInt::from(10).into(),
            "192.168.1.0",
            &num_bigint::BigInt::from(50).into(),
        )
        .unwrap();
        println!("tm: {:?}", tm);
        println!("json: {}", tm.get_message().unwrap());
    }
}
